package net.takela.common.utils;

import org.apache.hc.client5.http.utils.Base64;

import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


/**
 * BA签名验证相关Utils,http://wiki.sankuai.com/pages/viewpage.action?pageId=29755412
 *
 * @author mazhao
 * @version 1.0
 * created at 2013-08-01
 */
public class BasicAuthorizationUtils {

    /**
     * HTTP_HEADER_DATE
     */
    public static final String HTTP_HEADER_DATE = "Date";
    public static final String HTTP_HEADER_AUTHORIZATION = "Authorization";
    public static final String AUTH_METHOD = "MWS";
    public static final String HTTP_HEADER_TIME_ZONE = "GMT";
    public static final String HTTP_HEADER_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss z";

    public static final String ALGORITHM_HMAC_SHA1 = "HmacSHA1";

    private BasicAuthorizationUtils() {}

    /**
     * 生成签名认证参数
     * @param method HTTP请求方法，取值包括：PUT、GET、POST、DELETE
     * @param uri    HTTP请求URL Path
     * @param clientId 客户端
     * @param clientSecret 密钥
     *
     * @return Map，其中携带了Date和Authorization参数 Date: GMT格式编排的日期 Authorization: 签名认证信息
     */
    public static Map<String, String> generateAuthAndDateHeader(String method, String uri, String clientId, String clientSecret) {
        Map<String, String> map = new HashMap<>();
        Date sysdate = new Date();
        SimpleDateFormat df = new SimpleDateFormat(HTTP_HEADER_DATE_FORMAT, Locale.US);
        df.setTimeZone(TimeZone.getTimeZone(HTTP_HEADER_TIME_ZONE));
        String date = df.format(sysdate);
        String toSign = method.toUpperCase() + " " + uri + "\n" + date;
        String encoding;
        try {
            encoding = getSignature(toSign.getBytes(), clientSecret.getBytes());
        } catch (Exception e) {
            return map;
        }
        String authorization = AUTH_METHOD + " " + clientId + ":" + encoding;
        map.put(HTTP_HEADER_AUTHORIZATION, authorization);
        map.put(HTTP_HEADER_DATE, date);
        return map;
    }

    /**
     * 生产签名
     *
     * @param data
     * @param key
     * @return
     * @throws GeneralSecurityException
     */
    private static String getSignature(byte[] data, byte[] key) throws GeneralSecurityException {
        SecretKeySpec signingKey = new SecretKeySpec(key, ALGORITHM_HMAC_SHA1);
        Mac mac = Mac.getInstance(ALGORITHM_HMAC_SHA1);
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(data);
        return Base64.encodeBase64String(rawHmac);
    }

    /**
     * 从请求的Http Header Authorization解析client_id
     * @param authorization http header
     * @return client_id
     */
    public static String parseClientId(String authorization) {
        String[] strings = authorization.split(" ");
        if (strings.length != 2) {
            return null;
        }
        if (!Objects.equals(strings[0], AUTH_METHOD)) {
            return null;
        }
        String[] cs = strings[1].split(":");
        if (cs.length != 2) {
            return null;
        }
        return cs[0];
    }

    /**
     *
     * @param method string
     * @param uri string
     * @param clientId string
     * @param secret string
     * @param date string
     * @return string
     */
    public static String generateAuthorization(String method, String uri, String clientId, String secret, String date) {
        String toSign = method.toUpperCase() + " " + uri + "\n" + date;
        String encoding;
        try {
            encoding = getSignature(toSign.getBytes(), secret.getBytes());
        } catch (Exception e) {
            return null;
        }
        return AUTH_METHOD + " " + clientId + ":" + encoding;
    }
}